require ( 'Fluid' )

--
-- Converts mouse coords to grid
--
function ToGrid ( x, y )
	local lx = math.floor ( ( x - grid_offset_x + tile / 2 ) / tile )
	local ly = math.floor ( ( y - grid_offset_y + tile / 2 ) / tile )

	if ly >= 1 and ly <= H and lx >= 1 and lx <= W then
		return lx, ly
	end
	return nil, nil
end

--
-- Grid representation
--
W = 120
H = 80

f = Fluid ( W, H, 0, 0.00005 )

--
-- Visual representation
--
tile = 5

--display:create ( "Fluid Demo", tile * W, tile * H, 32, true )

local dm = DisplayMode ( )
dm.width = tile * W
dm.height = tile * H
dm.depth = 32
dm.windowed = true
display:set_mode ( dm )


grid_offset_x = -W * tile / 2 - tile / 2
grid_offset_y = -H * tile / 2 - tile / 2

grid = Sprite ( grid_offset_x, grid_offset_y )
grid.depth = -1
display.viewport:add_child ( grid )

c = Canvas ( )
c:rectangle ( tile, tile )
c:set_fill_style ( WHITE, 1 )
c:fill ( )

sprites = {}
for y = 1, H, 1 do
	for x = 1, W, 1 do
		local s = Sprite ( grid_offset_x + x * tile, grid_offset_y + y * tile )
		s.canvas = c
		display.viewport:add_child ( s )
		table.insert ( sprites, s )
	end
end

function PaintGrid ( )
	grid.canvas:clear ( )

	local h = f:GetHighestDensity ( )
	local l = f:GetLowestDensity ( )
	
	for x = 1, W, 1 do
		for y = 1, H, 1 do	        
			local d = f:GetDensity ( x, y )
			local i = ( y - 1 ) * W + ( x - 1 ) + 1

			sprites[i].alpha = d / h
--[[
			-- render the force field
			local u, v = f:GetForce ( x, y )
			grid.canvas:move_to ( x * tile, y * tile ) -- C crash if this line is missing
			grid.canvas:rel_line_to ( u * tile * 4, v * tile * 4 )
			grid.canvas:set_line_style ( 1, RED, 1 )
			grid.canvas:stroke ( )
]]
		end
	end
end

--
-- Edits the grid with the mouse
--
--
mouse.on_move = function ( mouse, dx, dy )
	if mouse:is_down ( MBUTTON_LEFT ) == true then
		local x, y = ToGrid ( mouse.xaxis, mouse.yaxis )
		if x and y then
	        	local u = dx / tile
        		local v = dy / tile
	        	f:SetForce ( x, y, u, v )
		end
	end
end

local timer = Timer ( )
timer:start ( 16, true )

timer.on_tick = function ( timer )
	if mouse:is_down ( MBUTTON_RIGHT ) == true then
		local x, y = ToGrid ( mouse.xaxis, mouse.yaxis )
		if x and y then
			local d = f:GetDensity ( x, y )
			f:SetDensity ( x, y, d + 1 )
		end
	end

	local d = timer:get_delta_ms ( ) / 1000
	f:Solve ( d, 20 )
	PaintGrid ( )
end


-- help
font = Font ( )
font:load_system ( "Courier New", 12 )
sprite = Sprite ( )
display.viewport:add_child ( sprite )
sprite.depth = -10000
sprite.canvas:set_font ( font, YELLOW )

help = true

htimer = Timer ( )
htimer:start ( 1, true )
htimer.on_tick = function ( htimer )
	local lh = font:get_height ( )
	local x = -tile * W / 2 + ( lh - font:get_size ( ) )
	local y = tile * H / 2
	local fps = 1000 / htimer:get_delta ( )
	local c = sprite.canvas
	c:clear ( )

	if help == false then
		return
	end

	c:move_to ( x, y - lh )
	c:write ( "Interactive fluid dynamics simulation " )
	c:move_to ( x, y - lh * 2 )
	c:write ( "FPS - " .. fps )
	c:move_to ( x, y - lh * 3 )
	c:write ( "Resolution - " .. W .. "x" .. H )
	c:move_to ( x, y - lh * 4 )
	c:write ( "MBUTTON_LEFT - stir" )
	c:move_to ( x, y - lh * 5 )
	c:write ( "MBUTTON_RIGHT - pour" )
	c:move_to ( x, y - lh * 6 )
	c:write ( "KEY_F1 - toggle help" )
end


if keyboard then
	keyboard.on_press = function ( keyboard, key )
		if key == KEY_F1 then
			help = not help
		end
	end
end